﻿using System;
using System.Collections.Generic;
using System.Text;

namespace XSSDP
{
    public enum SearchTarget { SSDP_ALL, UPNP_ROOTDEVICE, UUID }
    public enum MessageType { SearchRequest, SearchResponse, Alive, Byebye };

    public class ssdp
    {
        public static string SDDP_ADDRESS = "239.255.255.250";
        public static int SDDP_PORT = 1900;
        public int MX;
        public SearchTarget ST;
        public string DeviceUUID;
        public MessageType MessageType;
        
        public ssdp()
        {
        }
        public ssdp(MessageType messageType)
        {
            MessageType = messageType;
        }

        public static ssdp Parse(byte[] buffer,int length)
        {
            ssdp ret = new ssdp();
            string txt = System.Text.Encoding.Default.GetString(buffer, 0, length);
            try
            {
                if (txt.StartsWith("M-SEARCH"))
                {
                    ret.MessageType = MessageType.SearchRequest;
                    ret.ParseSearchRequest(txt);
                }
                if (txt.StartsWith("HTTP/1.1 200 OK"))
                {
                    ret.MessageType = MessageType.SearchResponse;
                    ret.ParseSearchResponse(txt);
                }
                if (txt.StartsWith("NOTIFY"))
                {
                    ret.ParseNotify(txt);
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            return ret;
        }

        public byte[] GetCommandBytes()
        {
            string cmd = "";
            if (MessageType == MessageType.SearchRequest) cmd = BuildSearchRequest();
            if (MessageType == MessageType.SearchResponse) cmd = BuildSearchResponse();
            if (MessageType == MessageType.Alive) cmd = BuildAlive();
            if (MessageType == MessageType.Byebye) cmd = BuildByebye();
            return System.Text.Encoding.Default.GetBytes(cmd);
        }

        /*
        M-SEARCH * HTTP/1.1
        HOST: 239.255.255.250:1900
        MAN: "ssdp:discover"
        MX: seconds to delay response
        ST: search target
         */
        private string BuildSearchRequest(){
            string ret = "M-SEARCH * HTTP/1.1" + "\r\n" + "HOST: 239.255.255.250:1900" + "\r\n" + "MAN: \"ssdp:discover\"" + "\r\n";;
            ret += BuildST();
            return ret;
        }

        private string BuildST(){
            string ret = "ST:";
            if (ST == SearchTarget.SSDP_ALL)
            {
                ret += "ssdp:all";
            }
            if (ST == SearchTarget.UPNP_ROOTDEVICE)
            {
                ret += "upnp:rootdevice";
            }
            if (ST == SearchTarget.UUID)
            {
                ret += "uuid:" + DeviceUUID;
            }
            ret += "\r\n";
            return ret;
        }

        private void ParseSearchRequest(string txt)
        {
            string[] lines = txt.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {
                if (line.StartsWith("MX"))
                {
                    this.MX = int.Parse(line.Split(':')[1]);
                }
                if (line.StartsWith("ST"))
                {
                    ParseST(line);
                }
            }
        }

        private void ParseST(string line)
        {
            if (line.IndexOf("ssdp:all") != -1)
            {
                this.ST = SearchTarget.SSDP_ALL;
            }
            if (line.IndexOf("upnp:rootdevice") != -1)
            {
                this.ST = SearchTarget.UPNP_ROOTDEVICE;
            }
            if (line.IndexOf("uuid:") != -1)
            {
                this.ST = SearchTarget.UUID;
                this.DeviceUUID = line.Split(':')[2];
            }
        }

        public int MaxAge;
        public string Location;
        public string OSVersion;
        public string ProductVersion;
        public string USN;
        /*  
        HTTP/1.1 200 OK
        CACHE-CONTROL: max-age = seconds until advertisement expires
        DATE: when reponse was generated
        EXT:
        LOCATION: URL for UPnP description for root device
        SERVER: OS/Version UPNP/1.0 product/version
        ST: search target
        USN: advertisement UUID
         */
        private string BuildSearchResponse()
        {
            string ret = "HTTP/1.1 200 OK" + "\r\n" + "CACHE-CONTROL: max-age = " + MaxAge + "\r\n" + "DATE: " + System.DateTime.Now.ToString() + "\r\n";
            ret += "EXT:" + "\r\n" + "LOCATION:" + Location + "\r\n" + "SERVER:" + OSVersion + " UPNP/1.0 " + ProductVersion + "\r\n";
            ret += BuildST();
            ret += "USN:" + USN + "\r\n";
            return ret;
        }

        private void ParseSearchResponse(string txt)
        {
            string[] lines = txt.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {
                if (line.StartsWith("CACHE-CONTROL"))
                {
                    try
                    {
                        this.MaxAge = int.Parse(line.Split(':')[1].Split('=')[1]);
                    }
                    catch (Exception ex)
                    {
                        ;
                    }
                }
                if (line.StartsWith("LOCATION"))
                {
                    this.Location = line.Split(':')[1].Trim();
                }
                if (line.StartsWith("SERVER"))
                {
                    string server = line.Split(':')[1].Trim();
                    string[] paras = server.Split(' ');
                    this.OSVersion = paras[0];
                    if (paras.Length >= 3)
                        this.ProductVersion = paras[2];
                }
                if (line.StartsWith("ST"))
                {
                    ParseST(line);
                }
                if (line.StartsWith("USN"))
                {
                    this.USN = line.Split(':')[1].Trim();
                }
            }
        }

        /* 
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age = seconds until advertisement expires
        LOCATION: URL for UPnP description for root device
        NT: search target
        NTS: ssdp:alive
        USN: advertisement UUID
         */
        public string NTS;
        public string NT;
        private string BuildAlive()
        {
            NTS = "ssdp:alive";
            return BuildNotify();
        }

        private string BuildByebye()
        {
            NTS = "ssdp:byebye";
            return BuildNotify();
        }

        private string BuildNotify()
        {
            string ret = "NOTIFY * HTTP/1.1" + "\r\n" + "HOST: 239.255.255.250:1900" + "\r\n" + "CACHE-CONTROL: max-age = " + MaxAge + "\r\n";
            ret += "LOCATION:" + Location + "\r\n" + "NT:" + NT + "\r\n";
            ret += "NTS:" + NTS + "\r\n";
            ret += "USN:" + USN + "\r\n";
            return ret;
        }

        private void ParseNotify(string txt)
        {
            string[] lines = txt.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string line in lines)
            {
                if (line.StartsWith("CACHE-CONTROL"))
                {
                    this.MaxAge = int.Parse(line.Split(':')[1]);
                }
                if (line.StartsWith("LOCATION"))
                {
                    this.Location = line.Split(':')[1].Trim();
                }
                if (line.StartsWith("NT"))
                {
                    this.NT = line.Split(':')[1].Trim();
                }
                if (line.StartsWith("NTS"))
                {
                    this.NTS = line.Split(':')[1].Trim();
                    if (NTS == "ssdp:alive")
                        this.MessageType = MessageType.Alive;
                    if (NTS == "ssdp:byebye")
                        this.MessageType = MessageType.Byebye;
                }
                if (line.StartsWith("USN"))
                {
                    this.USN = line.Split(':')[1].Trim();
                }
            }
        }
        

    }
}
